﻿using System;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.IO;
using UnityEngine.Rendering;

namespace Obi{

    public static class ObiEditorUtils
	{
        static GUIStyle separatorLine;
        static GUIStyle toggleablePropertyGroup;
        static GUIStyle boldToggle;

        public static GUIStyle GetSeparatorLineStyle()
        {
            if (separatorLine == null)
            {
                separatorLine = new GUIStyle(EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene).box);
                separatorLine.normal.background = Resources.Load<Texture2D>("SeparatorLine");
                separatorLine.border = new RectOffset(3, 3, 0, 0);
                separatorLine.padding = new RectOffset(0, 0, 0, 0);
                separatorLine.margin = new RectOffset(0, 0, 0, 0);
                separatorLine.fixedHeight = 3;
                separatorLine.stretchWidth = true;
            }
            return separatorLine;
        }

        public static GUIStyle GetToggleablePropertyGroupStyle()
        {
            if (toggleablePropertyGroup == null)
            {
                toggleablePropertyGroup = new GUIStyle();
                toggleablePropertyGroup.normal.background = Resources.Load<Texture2D>("ToggleableGroupBg"); 
                toggleablePropertyGroup.border = new RectOffset(3, 3, 3, 3);
                toggleablePropertyGroup.padding = new RectOffset(0, 0, 0, 0);
                toggleablePropertyGroup.margin = new RectOffset(0, 0, 3, 3);
            }
            return toggleablePropertyGroup;
        }

        public static GUIStyle GetBoldToggleStyle()
        {
            if (boldToggle == null)
            {
                boldToggle = new GUIStyle(EditorStyles.toggle);
                boldToggle.fontStyle = FontStyle.Bold;
            }
            return boldToggle;
        }

        public static void SaveMesh (Mesh mesh, string title, string name, bool makeNewInstance = true, bool optimizeMesh = true) {

			string path = EditorUtility.SaveFilePanel(title, "Assets/", name, "asset");
			if (string.IsNullOrEmpty(path)) return;
	        
			path = FileUtil.GetProjectRelativePath(path);

			Mesh meshToSave = (makeNewInstance) ? GameObject.Instantiate(mesh) as Mesh : mesh;

			if (optimizeMesh)
			     MeshUtility.Optimize(meshToSave);
	        
			AssetDatabase.CreateAsset(meshToSave, path);
			AssetDatabase.SaveAssets();
		}

        public static void PlaceActorRoot(GameObject element, MenuCommand menuCommand)
        {
            GameObject parent = menuCommand.context as GameObject;

            if (parent == null)
            {
                parent = GetOrCreateSolverObject();
            }

            if (parent.GetComponentsInParent<ObiSolver>(true).Length == 0)
            {
                // Create solver under context GameObject,
                // and make that be the parent which actor is added under.
                GameObject solver = CreateNewSolver();
                solver.transform.SetParent(parent.transform, false);
                parent = solver;
            }

            // The element needs to be already in its destination scene when the
            // RegisterCreatedObjectUndo is performed; otherwise the scene it was created in is dirtied.
            SceneManager.MoveGameObjectToScene(element, parent.scene);

            Undo.RegisterCreatedObjectUndo(element, "Create " + element.name);

            if (element.transform.parent == null)
                Undo.SetTransformParent(element.transform, parent.transform, "Parent " + element.name);

            GameObjectUtility.EnsureUniqueNameForSibling(element);

            // We have to fix up the undo name since the name of the object was only known after reparenting it.
            Undo.SetCurrentGroupName("Create " + element.name);

            GameObjectUtility.SetParentAndAlign(element, parent);
            Selection.activeGameObject = element;
        }

        // Helper function that returns a Solver GameObject; preferably a parent of the selection, or other existing Canvas.
        private static GameObject GetOrCreateSolverObject()
        {
            GameObject selectedGo = Selection.activeGameObject;

            // Try to find a gameobject that is the selected GO or one if its parents.
            ObiSolver solver = (selectedGo != null) ? selectedGo.GetComponentInParent<ObiSolver>() : null;
            if (IsValidSolver(solver))
                return solver.gameObject;

            // No solver in selection or its parents? Then use any valid solver.
            // We have to find all loaded solvers, not just the ones in main scenes.
            ObiSolver[] solverArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType<ObiSolver>();
            for (int i = 0; i < solverArray.Length; i++)
                if (IsValidSolver(solverArray[i]))
                    return solverArray[i].gameObject;

            // No solver in the scene at all? Then create a new one.
            return CreateNewSolver();
        }

        public static GameObject CreateNewSolver()
        {
            // Root for the actors.
            var root = new GameObject("Obi Solver", typeof(ObiSolver));

            // Works for all stages.
            StageUtility.PlaceGameObjectInCurrentStage(root);
            Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);

            return root;
        }

        static bool IsValidSolver(ObiSolver solver)
        {
            if (solver == null || !solver.gameObject.activeInHierarchy)
                return false;

            if (EditorUtility.IsPersistent(solver) || (solver.hideFlags & HideFlags.HideInHierarchy) != 0)
                return false;

            if (StageUtility.GetStageHandle(solver.gameObject) != StageUtility.GetCurrentStageHandle())
                return false;

            return true;
        }

        public static void DoPropertyGroup(GUIContent content, System.Action action)
        {
            EditorGUILayout.BeginVertical(GetToggleablePropertyGroupStyle());
            {
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField(content, EditorStyles.boldLabel); 
                EditorGUILayout.EndHorizontal();

                if (action != null)
                {
                    EditorGUI.indentLevel++;
                    action();
                    EditorGUI.indentLevel--;
                }
            }
            EditorGUILayout.EndVertical();
        }

        public static void DoToggleablePropertyGroup(SerializedProperty enabledProperty, GUIContent content, System.Action action)
        {
            bool enabled = GUI.enabled;
            GUI.enabled &= enabledProperty.boolValue;
            EditorGUILayout.BeginVertical(GetToggleablePropertyGroupStyle());
            GUI.enabled = enabled;
            {
                EditorGUILayout.BeginHorizontal();
                    enabledProperty.boolValue = EditorGUILayout.ToggleLeft(content,enabledProperty.boolValue,EditorStyles.boldLabel);
                EditorGUILayout.EndHorizontal();

                if (enabledProperty.boolValue && action != null)
                {
                    EditorGUI.indentLevel++;
                    action();
                    EditorGUI.indentLevel--;
                }
            }
            EditorGUILayout.EndVertical();
        }

        public static int DoToolBar(int selected, GUIContent[] items)
        {
            // Keep the selected index within the bounds of the items array
            selected = selected < 0 ? 0 : selected >= items.Length ? items.Length - 1 : selected;

            GUIStyle style = GUI.skin.FindStyle("Button");

            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            for (int i = 0; i < items.Length; i++)
            {
                if (i == 0 && items.Length > 1)
                    style = GUI.skin.FindStyle("ButtonLeft");
                else if (items.Length > 1 && i == items.Length-1)
                    style = GUI.skin.FindStyle("ButtonRight");
                else if (i > 0)
                    style = GUI.skin.FindStyle("ButtonMid");
                    

                // Display toggle. Get if toggle changed.
                bool change = GUILayout.Toggle(selected == i, items[i],style,GUILayout.Height(24));
                // If changed, set selected to current index.
                if (change)
                    selected = i;
            }
            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();

            // Return the currently selected item's index
            return selected;
        }

        public static void DrawArrowHandle(Vector3 posA, Vector3 posB, float headAngle = 30, float headLength = 0.18f)
        {
            Handles.DrawLine(posA, posB);

            var look = Quaternion.LookRotation(posA - posB, Camera.current.transform.forward);
            var one = look * Quaternion.Euler(0, 180 + headAngle, 0) * new Vector3(0, 0, 1);
            var two = look * Quaternion.Euler(0, 180 - headAngle, 0) * new Vector3(0, 0, 1);

            var sizeA = HandleUtility.GetHandleSize(posA) * headLength;
            Handles.DrawLine(posA, posA + one * sizeA);
            Handles.DrawLine(posA, posA + two * sizeA);

            var sizeB = HandleUtility.GetHandleSize(posB) * headLength;
            Handles.DrawLine(posB, posB - one * sizeB);
            Handles.DrawLine(posB, posB - two * sizeB);
        }

        public static Material GetDefaultMaterial()
        {
            if (GraphicsSettings.defaultRenderPipeline != null)
            {
                return GraphicsSettings.defaultRenderPipeline.defaultMaterial;
            }
            else
            {
                return AssetDatabase.GetBuiltinExtraResource<Material>("Default-Diffuse.mat");
            }
        }
    }
}


